﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Xml;
using System.Reflection;

using Microsoft.SharePoint;
using Microsoft.SharePoint.Publishing;
using Microsoft.SharePoint.StsAdmin;
using Microsoft.Office.Server;

namespace GoodStuff.SharePoint2007.Stsadm
{
    /// <summary>
    /// 
    /// </summary>
    /// <remarks>
    /// 1) Copy stsadmcommands.masterpagecommands.xml to 12\CONFIG
    /// 2) Copy GoodStuff.SharePoint to GAC or 12\BIN (NOT 12\CONFIG\BIN!!)
    ///     --> When to local bin, think about version conflicts with GAC!
    /// 3) Test with "stsadm -help showmasterpage" or "stsadm -help setmasterpage"
    /// 
    /// Unhandled Exception: System.ArgumentNullException: Value cannot be null. Parameter name: type
    /// at System.Activator.CreateInstance(Type type, Boolean nonPublic)
    /// --> your .dll is not in \12\BIN (not \12\CONFIG\BIN) or GAC
    /// </remarks>
    public class MasterPageCommands : ISPStsadmCommand
    {
        const string showmasterpagecommand = "showmasterpage";
        const string showmasterpageparameter_url = "url";

        const string setmasterpagecommand = "setmasterpage";
        const string setmasterpageparameter_url = "url";
        const string setmasterpageparameter_masterurl = "masterpageurl";
        const string setmasterpageparameter_defaulturl = "defaultmasterurl";
        const string setmasterpageparameter_customurl = "custommasterurl";

        #region ISPStsadmCommand Members

        /// <summary>
        /// Called when the user issues the "stsadm -help xxx" command, where "xxx" is one of the
        /// commands supported by this class.
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        public string GetHelpMessage(string command)
        {
            const string indent = "        ";
            StringBuilder helptext = new StringBuilder();

            switch (command.ToLower())
            {
                case showmasterpagecommand:
                    helptext.AppendFormat("-{0} <Url of site>\n", showmasterpageparameter_url);
                    break;

                case setmasterpagecommand:
                    helptext.AppendFormat("-{0} <Url of site>\n", setmasterpageparameter_url);
                    helptext.AppendFormat("{0}[-{1} <Url of master page> | Parent]\n", indent, setmasterpageparameter_masterurl);
                    helptext.AppendFormat("{0}[-{1} <Url of default master page> | Parent]\n", indent, setmasterpageparameter_defaulturl);
                    helptext.AppendFormat("{0}[-{1} <Url of custom master page> | Parent]\n", indent, setmasterpageparameter_customurl);
                    helptext.Append("\n");
                    helptext.AppendFormat("Must specify one of the three optional master url parameters.\n");
                    helptext.AppendFormat("Use '{0}' to change both default and custom to same value.\n", setmasterpageparameter_masterurl);
                    helptext.AppendFormat("Use 'Parent' to change to the same master used by parent site; only valid for subsites.\n");
                    break;

                default:
                    helptext.AppendFormat("{0} {1} command not recognized\n", indent, command);
                    break;
                //throw new InvalidOperationException();
            }

            return helptext.ToString();
        }

        /// <summary>
        /// Called when the user issues the "stsadm -o xxx" command, where "xxx" is one of the
        /// commands supported by this class (as specified in the stsadmcommands.eep_audiences.xml file).
        /// </summary>
        /// <param name="command">The "xxx" command which follows the "-o" parameter</param>
        /// <param name="keyValues">The remaining parameters from the command line</param>
        /// <param name="output">Optional output back to the user</param>
        /// <returns>-2 - Syntax error
        /// -1 - Other error
        /// 0 - Success</returns>
        public int Run(string command, StringDictionary keyValues, out string output)
        {
            int returnValue = 0;
            output = "";

            switch (command.ToLower())
            {
                case showmasterpagecommand:
                    returnValue = ShowMasterPageParameterCheck(keyValues, out output);
                    if (returnValue == 0)
                    {
                        returnValue = ShowMasterPage(keyValues, ref output);
                    }
                    break;

                case setmasterpagecommand:
                    returnValue = SetMasterPageParameterCheck(keyValues, out output);
                    if (returnValue == 0)
                    {
                        returnValue = SetMasterPage(keyValues, ref output);
                    }
                    break;

                default:
                    returnValue = -1;
                    break;
            }

            return returnValue;
        }

        #endregion

        private int ShowMasterPageParameterCheck(StringDictionary keyValues, out string output)
        {
            // Initialize variables
            output = "";

            //Check Required parameters
            string[] requiredAll = { showmasterpageparameter_url };

            return ParameterCheck(keyValues, requiredAll, null, ref output);
        }

        private int SetMasterPageParameterCheck(StringDictionary keyValues, out string output)
        {
            // Initialize variables
            output = "";

            //Check Required parameters
            string[] requiredAll = { setmasterpageparameter_url };
            string[] requiredOne = { setmasterpageparameter_masterurl, setmasterpageparameter_defaulturl, setmasterpageparameter_customurl };

            return ParameterCheck(keyValues, requiredAll, requiredOne, ref output);
        }

        private int ShowMasterPage(StringDictionary keyValues, ref string output)
        {
            // Initialize variables
            string webUrl = "";
            string rootUrl = "";
            string relativeUrl = "";
            SPSite site = null;
            SPWeb web = null;

            int returnValue = 0;
            System.Text.StringBuilder outMsg = new StringBuilder();

            try
            {
                // Since we have already checked the parameters, we can assume that they exist and have values
                webUrl = keyValues[setmasterpageparameter_url];
                webUrl = webUrl.TrimEnd(new char[3] { '/', ' ', '\t' });

                if (Utilities.rexUrlRootRelative.Match(webUrl).Success)
                {
                    rootUrl = Utilities.rexUrlRootRelative.Match(webUrl).Result("$1");
                    relativeUrl = Utilities.rexUrlRootRelative.Match(webUrl).Result("$2");
                }
                else
                    throw new ArgumentException("<WebUrl> not formatted properly.");

                site = new SPSite(webUrl);
                web = site.OpenWeb(relativeUrl);

                if (!web.Exists)
                    throw new ApplicationException(webUrl + " does not exist.");

                PublishingWeb publishingWeb = null;

                // Publishing sites have inheritance
                if (PublishingWeb.IsPublishingWeb(web))
                {
                    //Set publishing web
                    publishingWeb = PublishingWeb.GetPublishingWeb(web);
                    if (publishingWeb.IsRoot)
                        outMsg.Append("This is a publishing site collection.\n");
                    else
                        outMsg.Append("This is a publishing subsite.\n");

                    outMsg.Append("Site:            " + publishingWeb.Url + "\n");
                    outMsg.Append("MasterUrl:       " + publishingWeb.MasterUrl.Value + "\n");
                    outMsg.Append("  IsInheriting:  " + publishingWeb.MasterUrl.IsInheriting + "\n");
                    outMsg.Append("CustomMasterUrl: " + publishingWeb.CustomMasterUrl.Value + "\n");
                    outMsg.Append("  IsInheriting:  " + publishingWeb.CustomMasterUrl.IsInheriting + "\n");
                }
                else
                {
                    if (web.IsRootWeb)
                        outMsg.Append("This is a site collection.\n");
                    else
                        outMsg.Append("This is a subsite.\n");

                    outMsg.Append("Site:            " + web.Url + "\n");
                    outMsg.Append("MasterUrl:       " + web.MasterUrl + "\n");
                    outMsg.Append("CustomMasterUrl: " + web.CustomMasterUrl + "\n");
                }

            }
            catch (Exception ex)
            {
                outMsg.Append("\n" + ex.Message + "\n");
                returnValue = -1;
            }
            finally
            {
                if (web != null) web.Dispose();
                output = output += outMsg.ToString();
            }
            return returnValue;
        }


        private int SetMasterPage(StringDictionary keyValues, ref string output)
        {
            // Initialize variables
            string siteUrl = "";
            string webUrl = "";
            string rootUrl = "";
            string relativeUrl = "";
            string defaultUrl = "";
            string customUrl = "";
            bool inherit = false;
            SPSite site = null;
            SPWeb web = null;
            PublishingWeb publishingWeb = null;

            int returnValue = 0;
            System.Text.StringBuilder outMsg = new StringBuilder();

            try
            {
                // Since we have already checked the parameters, we can assume that they exist and have values
                webUrl = keyValues[setmasterpageparameter_url];
                if (Utilities.rexUrlRootRelative.Match(webUrl).Success)
                {
                    rootUrl = Utilities.rexUrlRootRelative.Match(webUrl).Result("$1");
                    relativeUrl = Utilities.rexUrlRootRelative.Match(webUrl).Result("$2");
                }
                else
                    throw new ArgumentException("<WebUrl> not formatted properly.");

                site = new SPSite(webUrl);
                web = site.OpenWeb(relativeUrl);

                if ((web == null) || (!web.Exists))
                    throw new ApplicationException(webUrl + " does not exist.");

                siteUrl = web.Url;


                // Publishing sites have inheritance
                if (PublishingWeb.IsPublishingWeb(web))
                {
                    publishingWeb = PublishingWeb.GetPublishingWeb(web);

                    if (keyValues.ContainsKey(setmasterpageparameter_masterurl))
                    {
                        defaultUrl = keyValues[setmasterpageparameter_masterurl];
                        customUrl = defaultUrl;
                        inherit = (defaultUrl == "Parent");
                        if (inherit)
                        {
                            if (!publishingWeb.IsRoot)
                            {
                                defaultUrl = publishingWeb.ParentPublishingWeb.MasterUrl.Value;
                                customUrl = publishingWeb.ParentPublishingWeb.CustomMasterUrl.Value;
                            }
                            else
                                throw new ApplicationException("This is a publishing site collection and does not have a parent.");
                        }

                        outMsg.Append("Setting default master page to \"" + defaultUrl + "\"\n");
                        outMsg.Append("Setting custom master page to \"" + customUrl + "\"\n");
                    }
                    if (keyValues.ContainsKey(setmasterpageparameter_defaulturl))
                    {
                        defaultUrl = keyValues[setmasterpageparameter_defaulturl];
                        inherit = (defaultUrl == "Parent");
                        if (inherit)
                        {
                            if (!publishingWeb.IsRoot)
                                defaultUrl = publishingWeb.ParentPublishingWeb.MasterUrl.Value;
                            else
                                throw new ApplicationException("This is a publishing site collection and does not have a parent.");
                        }
                        outMsg.Append("Setting default master page to \"" + defaultUrl + "\"\n");
                    }
                    if (keyValues.ContainsKey(setmasterpageparameter_customurl))
                    {
                        customUrl = keyValues[setmasterpageparameter_customurl];
                        inherit = (customUrl == "Parent");
                        if (inherit)
                        {
                            if (!publishingWeb.IsRoot)
                                customUrl = publishingWeb.ParentPublishingWeb.CustomMasterUrl.Value;
                            else
                                throw new ApplicationException("This is a publishing site collection and does not have a parent.");
                        }
                        outMsg.Append("Setting custom master page to \"" + customUrl + "\"\n");
                    }

                    outMsg.Append("for publishing site at \"" + siteUrl + "\"\n");

                    // Set the Master Page URLs and Inheritance (from Parent)
                    if (defaultUrl != "")
                    {
                        publishingWeb.MasterUrl.SetValue(defaultUrl);
                        publishingWeb.MasterUrl.SetInherit(inherit, false);
                    }

                    if (customUrl != "")
                    {
                        publishingWeb.CustomMasterUrl.SetValue(customUrl);
                        publishingWeb.CustomMasterUrl.SetInherit(inherit, false);
                    }

                    publishingWeb.Update();
                }


                // Not a publishing site
                else
                {
                    if (keyValues.ContainsKey(setmasterpageparameter_masterurl))
                    {
                        defaultUrl = keyValues[setmasterpageparameter_masterurl];
                        customUrl = defaultUrl;
                        inherit = (defaultUrl == "Parent");
                        if (inherit)
                        {
                            if (web.ParentWeb != null)
                            {
                                defaultUrl = web.ParentWeb.MasterUrl;
                                customUrl = web.ParentWeb.CustomMasterUrl;
                            }
                            else
                                throw new ApplicationException("This is a site collection and does not have a parent.");
                        }

                        outMsg.Append("Setting default master page to \"" + defaultUrl + "\"\n");
                        outMsg.Append("Setting custom master page to \"" + customUrl + "\"\n");
                    }
                    if (keyValues.ContainsKey(setmasterpageparameter_defaulturl))
                    {
                        defaultUrl = keyValues[setmasterpageparameter_defaulturl];
                        inherit = (defaultUrl == "Parent");
                        if (inherit)
                        {
                            if (web.ParentWeb != null)
                                defaultUrl = web.ParentWeb.MasterUrl;
                            else
                                throw new ApplicationException("This is a site collection and does not have a parent.");
                        }
                        outMsg.Append("Setting default master page to \"" + defaultUrl + "\"\n");
                    }
                    if (keyValues.ContainsKey(setmasterpageparameter_customurl))
                    {
                        customUrl = keyValues[setmasterpageparameter_customurl];
                        inherit = (customUrl == "Parent");
                        if (inherit)
                        {
                            if (web.ParentWeb != null)
                                customUrl = web.ParentWeb.CustomMasterUrl;
                            else
                                throw new ApplicationException("This is a site collection and does not have a parent.");
                        }
                        outMsg.Append("Setting custom master page to \"" + customUrl + "\"\n");
                    }

                    outMsg.Append("for site at \"" + siteUrl + "\"\n");

                    // Set the Master Page URLs
                    if (defaultUrl != "")
                        web.MasterUrl = defaultUrl;
                    if (customUrl != "")
                        web.CustomMasterUrl = customUrl;
                    web.Update();
                }

                outMsg.Append("\n");
            }
            catch (Exception ex)
            {
                outMsg.Append("\n" + ex.Message + "\n");
                returnValue = -1;
            }
            finally
            {
                if (web != null) web.Dispose();
                if (publishingWeb != null) publishingWeb.Close();
                output = output += outMsg.ToString();
            }

            if (returnValue == 0)
                return ShowMasterPage(keyValues, ref output);
            else
                return returnValue;
        }


        #region Helper Methods
        /// <summary>
        /// 
        /// </summary>
        /// <param name="keyValues">Pairs of commands and values passed from stsadm</param>
        /// <param name="requiredAll">List of commands, all of which are required</param>
        /// <param name="requiredOne">List of commands, at least one of which is required</param>
        /// <param name="output"></param>
        /// <returns></returns>
        private static int ParameterCheck(StringDictionary keyValues, string[] requiredAll, string[] requiredOne, ref string output)
        {
            int returnValue = 0;

            StringBuilder msg = new StringBuilder();

            if (requiredAll != null)
            {
                foreach (string parameter in requiredAll)
                {
                    if (!keyValues.ContainsKey(parameter))
                        msg.AppendFormat("The '{0}' parameter was not specified\n", parameter);
                    else if (keyValues[parameter] == "")
                        msg.AppendFormat("Value not supplied for '{0}' parameter.\n", parameter);
                }
            }

            if (requiredOne != null)
            {
                bool foundOne = false;
                foreach (string parameter in requiredOne)
                {
                    if (keyValues.ContainsKey(parameter))
                    {
                        foundOne = true;
                        if (keyValues[parameter] == "")
                            msg.AppendFormat("Value not supplied for '{0}' parameter.\n", parameter);
                    }
                }
                if (!foundOne)
                {
                    msg.Append("Must specify one of ");
                    foreach (string parameter in requiredOne)
                        msg.AppendFormat("'{0}' ", parameter);
                    msg.Append("\n");
                }
            }

            // Syntax error--let the user know by returning -2 and passing the message(s) back.
            if (msg.Length > 0)
            {
                returnValue = -2;
            }

            output = msg.ToString();
            return returnValue;
        }

        #endregion

    }
}
